package org.summerframework.component.mybites.config;

import org.summerframework.core.util.AnnotationAutoProxyCreator;
import org.summerframework.core.util.SpringContextUtil;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Role;
import org.springframework.core.Ordered;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.interceptor.*;

import javax.annotation.Resource;
import java.util.Arrays;

/**
 * @author 石超
 * @version v1.0.0
 */
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@AutoConfigureBefore({DataSourceAutoConfiguration.class, TransactionAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class TransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    @Resource
    private SpringContextUtil springContextUtil;

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionManagerCustomizers transactionManagerCustomizers() {
        return new TransactionManagerCustomizers(null) {
            @Override
            public void customize(PlatformTransactionManager transactionManager) {

                if (transactionManager instanceof DataSourceTransactionManager) {
                    DataSourceTransactionManager dataSourceTransactionManager = (DataSourceTransactionManager) transactionManager;
                    dataSourceTransactionManager.setGlobalRollbackOnParticipationFailure(false);
                }
            }
        };
    }

    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
    }


    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    @Bean("transactionInterceptor")
    public TransactionInterceptor txAdvice() {
        RuleBasedTransactionAttribute transactionAttribute = new RuleBasedTransactionAttribute();
        transactionAttribute.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        transactionAttribute.getRollbackRules().add(new RollbackRuleAttribute("exception"));

        DefaultTransactionAttribute defaultTransactionAttribute = new DefaultTransactionAttribute();
        defaultTransactionAttribute.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        defaultTransactionAttribute.setReadOnly(true);

        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();

        source.addTransactionalMethod("insert*", transactionAttribute);
        source.addTransactionalMethod("delete*", transactionAttribute);
        source.addTransactionalMethod("update*", transactionAttribute);
        source.addTransactionalMethod("edit*", transactionAttribute);
        source.addTransactionalMethod("upload*", transactionAttribute);
        source.addTransactionalMethod("add*", transactionAttribute);
        source.addTransactionalMethod("save*", transactionAttribute);
        source.addTransactionalMethod("query*", defaultTransactionAttribute);
        source.addTransactionalMethod("get*", defaultTransactionAttribute);
        source.addTransactionalMethod("select*", defaultTransactionAttribute);
        source.addTransactionalMethod("create*", transactionAttribute);

        TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
        transactionInterceptor.setTransactionAttributeSources(new AnnotationTransactionAttributeSource(), source);

        return transactionInterceptor;
    }

    @Lazy(false)
    @Bean("txProxy")
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public AnnotationAutoProxyCreator txAdviceAdvisor() {
        AnnotationAutoProxyCreator pointcut = new AnnotationAutoProxyCreator();
        pointcut.setAnnotations(Arrays.asList(Service.class));

        pointcut.setInterceptorNames("synchronizedInterceptor", "transactionInterceptor");
        pointcut.setProxyTargetClass(true);
        pointcut.setOrder(Ordered.LOWEST_PRECEDENCE);
        return pointcut;
    }
}
